import org.springframework.util.StringUtils
import scripts.Utils

includeTargets << new File( "scripts/Eclipse.groovy" )

// Import environment variables as ANT properties
Ant.property(environment:"env")

// Add the Groovyc task definition
Ant.taskdef (  name : 'groovyc' , classname : 'org.codehaus.groovy.ant.Groovyc' )

// Get the projectLocation property that was passed while invoking this script
def projectLocation = Ant.antProject.properties."projectLocation"

// Derive the project name, this is the name of the directory
def projectName = new File(projectLocation).name

println()
println("Building ${projectName}")
println()

// Get the JAVA_HOME environment variable because we need to add tools.jar to the classpath
def javaHome = Ant.antProject.properties."env.JAVA_HOME"

// Check if the projectLocation variable has been set
if (!StringUtils.hasText(projectLocation)) {
	throw new IllegalArgumentException("projectLocation property not set")
}

// Check if JAVA_HOME is set and that is points to a directory
if (!StringUtils.hasText(javaHome) || !new File(javaHome).exists() || !new File(javaHome).directory) {
	throw new IllegalArgumentException("JAVA_HOME is not set correctly")
}

// Define the location of tools.jar. This hopefully works across different JVM's
def tools_jar = "${javaHome}/lib/tools.jar"

// Check if tools.jar can be found on the expected path
if (!new File(tools_jar).exists() || !new File(tools_jar).file) {
	throw new IllegalArgumentException("Could not find lib/tools.jar in JAVA_HOME")
}

// Set the default task
task ( 'default': 'Default build target' ) {
	println("No target specified. 'install' is a good start.")
}

// Install: we mimic Maven ;-)
task ( install: 'Compile, run tests and package' ) {
	start_packaging()
}

// Set various source code directories
def src = "${projectLocation}/src"
def java_main_src = "${src}/main/java"
def java_test_src = "${src}/test/java"

// Set various target/output directories
def target = "${projectLocation}/target"
def java_main_target = "${target}/classes"
def java_test_target = "${target}/test-classes"
def test_reports_target = "${target}/test-reports"

def dist_dir = "${projectLocation}/dist"

// Gather the project properties from the Build.groovy file, if present
def projectProperties = Utils.getNestedProjectProperties( projectLocation )

def packaging = projectProperties.packaging ? projectProperties.packaging.toLowerCase() : "jar"

def getCompileLibs = {
	map, fetch, project, properties ->
	
	fetch(properties)?.each {
		lib ->
		
		def matcher = lib =~ /^([[a-zA-Z]+[\\.|-]]{2,}|[[a-zA-Z0-9]{2,}-]+)([a-zA-Z0-9]{1}[a-zA-Z0-9\.]+[-[a-zA-Z0-9\\.]+?]?)[\\.jar]?$/
		
		def libName
		def libVersion
		if (!matcher.matches()) {
			throw new IllegalArgumentException("[ERROR] Could not match [${lib}] to regular expression for dependency names")
		} else {
			def tmp = matcher.group(1)
			libName = tmp.substring(0, tmp.length() - 1)
			libVersion = matcher.group(2)
		}
		
		def libFound = false
		map.each {
			matcher = it.key =~ /^([[a-zA-Z]+[\\.|-]]{2,}|[[a-zA-Z0-9]{2,}-]+)([a-zA-Z0-9]{1}[a-zA-Z0-9\.]+[-[a-zA-Z0-9\\.]+?]?)[\\.jar]?$/
			
			if (matcher.matches()) {
				def group1 = matcher.group(1)
				def group2 = matcher.group(2)
				
				group1 = group1.substring(0, group1.length() - 1)
				
				if (libName.toLowerCase() == group1.toLowerCase()) {
					if (libVersion != group2) {
						throw new IllegalArgumentException("""[ERROR] Library conflict found in dependecies:
	${it.key} in project [${it.value}] clashes with ${lib} in project [${project}]				
""")
					} else {
						libFound = true
					}
				}
			} else {
				throw new IllegalArgumentException("[ERROR] Could not match [${lib}] to regular expression for dependency names")
			}
		}
		
		if (!libFound) {
			map."${lib}" = project
		}
	}
}

def compileLibs = [:]
Utils.traverseProjectProperties( getCompileLibs.curry( compileLibs, { it.libs } ), projectName, projectProperties )

def getDependencyLibs = {
	list, project, properties ->
	
	properties.deps?.each {
		entry ->
		
		def path = "${entry.key}/dist/${entry.key}.jar"
		
		if (!list.contains( path )) {
			list.add( path )
		}		
	}
}
def depLibs = []
Utils.traverseProjectProperties( getDependencyLibs.curry( depLibs ), projectName, projectProperties )


// Get project properties for dependencies

// Set the classpath based on the libs property, if set
Ant.path( id: 'compile.classpath' ) {
	Utils.createPathElementsForLibs( Ant, compileLibs.keySet(), "lib" )
	Utils.createPathElementsForLibs( Ant, depLibs, "projects" )
	pathelement( location: "src/main/resources" )
}


def testLibs = [:]
getCompileLibs( testLibs, { it.test_libs }, projectName, projectProperties )
Utils.traverseProjectProperties( getCompileLibs.curry( testLibs, { it.libs } ), projectName, projectProperties )


// Idem for the test classpath
Ant.path( id: 'compile_test.classpath' ) {
	Utils.createPathElementsForLibs( Ant, testLibs.keySet(), "lib" )
	Utils.createPathElementsForLibs( Ant, depLibs, "projects" )
	// Also add the compiled classes for this project
	pathelement( location: java_main_target )
	pathelement( location: "src/main/resources" )
	pathelement( location: "src/test/resources" )
}

// Create various target/output directories
task ( init: 'Create build directories' ) {
	Ant.mkdir( dir: java_main_target )
	Ant.mkdir( dir: java_test_target )
	Ant.mkdir( dir: test_reports_target )
	createEclipseProjectFiles()
	createWebXmlFile()
}

// Clean the project by removing the target directory
task ( clean: 'Clean build artifacts' ) {
	Ant.delete( dir: target )
}

task ( createWebXmlFile: "Create a web.xml file for web projects" ) {
	Ant.mkdir( dir: "${projectLocation}/src/web-app/WEB-INF" )
	def web_xml = new File( "${projectLocation}/src/web-app/WEB-INF/web.xml" )
	
	if (!web_xml.exists()) {
		web_xml << """<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>
</web-app>
"""
	}
}

task ( createEclipseProjectFiles : 'Create Eclipse project files' ) {
	def dot_project_file = new File( "${projectLocation}/.project" )
	def dot_classpath_file = new File( "${projectLocation}/.classpath" )
	
	def out1 = dot_project_file.newOutputStream()

	out1 << """<projectDescription>
  <name>${new File( projectLocation ).name}</name>
  <comment/>
  <projects>
"""
	projectProperties.deps?.each {
		out1 << "    <project>${it.key}</project>\n"
	}
	
	out1 << """  </projects>\n"""
	
	if (packaging == "jar") {
		out1 << """
  <buildSpec>
    <buildCommand>
      <name>org.eclipse.jdt.core.javabuilder</name>
    </buildCommand>
  </buildSpec>
  <natures>
    <nature>org.eclipse.jdt.core.javanature</nature>
  </natures>
"""
	} else if (packaging == "war") {
		out1 << """
	<buildSpec>
		<buildCommand>
			<name>org.eclipse.jdt.core.javabuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
		<buildCommand>
			<name>org.eclipse.wst.common.project.facet.core.builder</name>
			<arguments>
			</arguments>
		</buildCommand>
		<buildCommand>
			<name>org.eclipse.wst.validation.validationbuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
	</buildSpec>
	<natures>
		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
		<nature>org.eclipse.jdt.core.javanature</nature>
		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
	</natures>
"""	
	} else {
		throw new UnsupportedOperationException("[ERROR] Packaging mode [${packaging}] not supported")	
	}
	
	out1 << """</projectDescription>"""
	out1.close()
	
	def out2 = dot_classpath_file.newOutputStream()

	out2 << """<classpath>
  <classpathentry exported="true" kind="src" path="src/main/java" output="target/classes"/>
  <classpathentry exported="true" kind="src" path="src/main/resources" output="target/classes"/>
  <classpathentry exported="true" kind="src" path="src/test/java" output="target/test-classes"/>
  <classpathentry exported="true" kind="src" path="src/test/resources" output="target/test-classes"/>
  <classpathentry kind="output" path="target/classes"/>
"""
	if (packaging == "jar") {
		out2 << """  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\n"""
	} else if (packaging == "war") {
		out2 << """
  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre"/>
  <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
"""
	} else {
		throw new UnsupportedOperationException("[ERROR] Packaging mode [${packaging}] not supported")	
	}

	projectProperties.deps?.each {
		out2 << """  <classpathentry kind="src" path="/${it.key}"/>\n"""
	}
	
	def mainName = new File( new File( "${projectLocation}/../.." ).canonicalPath ).name.toUpperCase()
	testLibs.each {
		lib ->

		def sources
		def jar
		
		if (lib.key.endsWith(".jar") || lib.key.endsWith(".zip")) {
			sources = lib.key.substring(0, lib.key.length() - ".jar".length()) + "-sources.zip"
			jar = lib.key
		} else {
			sources = "${lib.key}-sources.zip"
			jar = "${lib.key}.jar"
		}
		
		def sourcePath
		def sourcesLocation = new File( "${projectLocation}/../../lib/${sources}" )
		if (sourcesLocation.exists() && sourcesLocation.file) {
			sourcePath = "${mainName}/lib/${sources}"
		}
		
		if (sourcePath) {
			out2 << """  <classpathentry kind="var" path="${mainName}/lib/${jar}" sourcepath="${sourcePath}"/>\n"""
		} else {
			out2 << """  <classpathentry kind="var" path="${mainName}/lib/${jar}"/>\n"""
		}
	}
	
	out2 << """</classpath>\n"""
	out2.close()
	
	def out3 = new File( "${projectLocation}/.settings/org.eclipse.jdt.core.prefs" ).newOutputStream()
	
	out3 << """
#${new Date()}
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.5
"""
	out3.close()
	
	println( "Make sure to add the ${mainName} classpath variable to your Eclipse preferences and have it pointed to ${new File( "${projectLocation}/../.." ).canonicalPath}" )
}

	if (packaging == "war") {
		new File( "${projectLocation}/.settings" ).mkdir()
		def out4 = new File( "${projectLocation}/.settings/org.eclipse.wst.common.component" ).newOutputStream()
		
		out4 << """<?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="${projectName}">
<wb-resource deploy-path="/" source-path="/src/web-app"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<property name="context-root" value="${projectName}"/>
<property name="java-output-path" value="target/classes"/>
</wb-module>
</project-modules>	
"""
		out4.close()

		def out5 = new File( "${projectLocation}/.settings/org.eclipse.wst.common.project.facet.core.xml" ).newOutputStream()
			
		out5 << """<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
  <fixed facet="jst.java"/>
  <fixed facet="jst.web"/>
  <installed facet="jst.web" version="2.4"/>
  <installed facet="jst.java" version="5.0"/>
</faceted-project>		
"""
		out5.close()

		def out6 = new File( "${projectLocation}/.settings/org.eclipse.jst.common.project.facet.core.prefs" ).newOutputStream()
		
		out6 << """#${new Date()}
classpath.helper/org.eclipse.jdt.launching.JRE_CONTAINER\\:\\:org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType\\:\\:jre/owners=jst.java\\:5.0
eclipse.preferences.version=1
"""
		out6.close()

	}
	

// Compile the main java sources
task ( compileJavaMain : 'Compile Java source code' ) {
	depends( init )
	Ant.javac( srcdir: java_main_src, destdir: java_main_target, debug: 'on', classpathref: 'compile.classpath' )
}

// Compile the test java sources
task ( compileJavaTest: 'Compile Java test code' ) {
	depends( compileJavaMain )
	Ant.javac( srcdir: java_test_src, destdir: java_test_target, debug: 'on', classpathref: 'compile_test.classpath' )
}

// Run the unit tests
task ( runTests: 'Run JUnit tests' ) {
	depends( compileJavaTest )
	Ant.junit( printsummary: 'yes', haltonfailure: 'yes', fork: 'yes', forkmode: 'once', dir: projectLocation, showoutput: 'yes' ) {
		classpath {
			path( refid: 'compile_test.classpath' )
			pathelement( location: java_test_target )
			pathelement( location: tools_jar )
		}
		
		formatter( type: 'xml' )
		
		batchtest( todir: test_reports_target ) {
			fileset( dir: java_test_target ) {
				include( name: '**/*Tests.class' )
				exclude( name: '**/Abstract*' )
			}
		}
	}
}

task( start_packaging: 'Start packing' ) {
	depends( runTests )
	if (packaging == "jar") {
		package_jar()
	} else if (packaging == "war") {
		package_war()
	} else {
		throw new UnsupportedOperationException("[ERROR] Packaging mode [${packaging}] not supported")
	}
}

task ( package_jar: 'Create a jar file for this beautiful project' ) {
	Ant.mkdir( dir: dist_dir )
	def dest_file = "${dist_dir}/${projectName}.jar"
	if (new File( dest_file ).exists()) {
		Ant.delete( file: dest_file )
	}
	Ant.jar( basedir: java_main_target, destfile: dest_file )
}

task ( package_war: 'Create a war file for this project' ) {
	package_jar()
	def dest_file = "${dist_dir}/${projectName}.war"
	if (new File( dest_file ).exists()) {
		Ant.delete( file: dest_file )
	}
	Ant.war( destfile: dest_file, webxml: "${projectLocation}/src/web-app/WEB-INF/web.xml" ) {
		Ant.lib( dir: dist_dir ) {
			Ant.include( name: "${projectName}.jar" )
		}
		depLibs.each {
			def libFile = new File( "projects/${it}" )
			def libDir = libFile.parentFile
			Ant.lib( dir: "${libDir.canonicalPath}" ) {
				Ant.include( name: libFile.name )
			}
		}
		Ant.lib( dir: "lib" ) {
			if (compileLibs.keySet().size() == 0 ) {
				Ant.exclude( name: "**/**" )
			}
			compileLibs.keySet().each {
				if (it.endsWith( ".jar" )) {
					Ant.include( name: "${it}" )
				} else {
					Ant.include( name: "${it}.jar" )
				}
			}
		}
		fileset( dir: "${projectLocation}/src/web-app" )
	}
}